﻿using GenericImporter;
using LLTreeManagementApplication.Export;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;

namespace GenericPersistance
{
    public class ProjectPersistence
    {
        // Xml tag for node, e.g. 'node' in case of <node></node>
        private const string XmlNodeTag = "node";

        // Xml attributes for node e.g. <node text="Asia" tag="" 
        // imageindex="1"></node>
        private const string XmlNodeTextAtt = "text";
        private const string XmlNodeTagAtt = "tag";
        private const string XmlNodeImageIndexAtt = "imageindex";
        private const string XmlNodeSelectedImageIndexAtt = "selectedimageindex";

        Importer _importer;

        #region Constructor
        public ProjectPersistence(Importer importer)
        {
            _importer = importer;
        }
        #endregion


        private void DeserializeTreeView(TreeView treeView, Stream stream)
        {
            XmlTextReader reader = null;
            try
            {
                treeView.Nodes.Clear();
                treeView.BeginUpdate();
                reader = new XmlTextReader(stream);
                TreeNode parentNode = null;
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        if (reader.Name == XmlNodeTag)
                        {
                            TreeNode newNode = new TreeNode();
                            bool isEmptyElement = reader.IsEmptyElement;

                            // loading node attributes
                            int attributeCount = reader.AttributeCount;
                            if (attributeCount > 0)
                            {
                                for (int i = 0; i < attributeCount; i++)
                                {
                                    reader.MoveToAttribute(i);
                                    SetAttributeValue(newNode,
                                                 reader.Name, reader.Value);
                                }
                            }
                            // add new node to Parent Node or TreeView
                            if (parentNode != null)
                                parentNode.Nodes.Add(newNode);
                            else
                                treeView.Nodes.Add(newNode);

                            // making current node 'ParentNode' if its not empty
                            if (!isEmptyElement)
                            {
                                parentNode = newNode;
                            }

                            reader.Read();

                            if (reader.Name == XmlNodeTagAtt)
                            {
                                reader.Read();
                                var value = reader.ReadOuterXml();

                                XmlSerializer serializer = _importer.GetXmlSerializer();//new XmlSerializer(typeof(DataNode));

                                using (StringReader writer = new StringReader(value))
                                {
                                    newNode.Tag = (DataDefinition.DataNode)serializer.Deserialize(writer);
                                }
                            }
                            else
                            {
                                throw new InvalidDataException("XML Node 'tag' is required");
                            }
                        }
                    }

                    // moving up to in TreeView if end tag is encountered
                    else if (reader.NodeType == XmlNodeType.EndElement)
                    {
                        if (reader.Name == XmlNodeTag)
                        {
                            parentNode = parentNode.Parent;
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.XmlDeclaration)
                    {
                        //Ignore Xml Declaration                    
                    }
                    else if (reader.NodeType == XmlNodeType.None)
                    {
                        return;
                    }
                    else if (reader.NodeType == XmlNodeType.Text)
                    {
                        parentNode.Nodes.Add(reader.Value);
                    }
                }
            }
            finally
            {
                // enabling redrawing of treeview after all nodes are added
                treeView.EndUpdate();
                reader.Close();
            }
        }

        /// <span class="code-SummaryComment"><summary>
        /// Used by Deserialize method for setting properties of 
        /// TreeNode from xml node attributes
        /// <span class="code-SummaryComment"></summary>
        private void SetAttributeValue(TreeNode node, string propertyName, string value)
        {
            if (propertyName == XmlNodeTextAtt)
            {
                node.Text = value;
            }
            else if (propertyName == XmlNodeImageIndexAtt)
            {
                node.ImageIndex = int.Parse(value);
            }
            else if (propertyName == XmlNodeSelectedImageIndexAtt)
            {
                node.SelectedImageIndex = int.Parse(value);
            }
            //else if (propertyName == XmlNodeTagAtt)
            //{
            //    XmlSerializer serializer = new XmlSerializer(typeof(DataNode));

            //    using (StringReader writer = new StringReader(value))
            //    {
            //        node.Tag = (DataNode)serializer.Deserialize(writer);
            //    }

            //    node.Tag = value;
            //}
        }

        private StringBuilder SerializeTreeView(TreeView treeView, string startElementName)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
            settings.Indent = true;
            settings.OmitXmlDeclaration = true;

            StringBuilder sb = new StringBuilder();
            XmlWriter writer = XmlWriter.Create(sb, settings);
            //XmlTextWriter writer = new XmlTextWriter(fileName,
            //                              System.Text.Encoding.ASCII);
            // writing the xml declaration tag
            writer.WriteStartDocument();
            //textWriter.WriteRaw("\r\n");
            // writing the main tag that encloses all node tags
            writer.WriteStartElement(startElementName);

            // save the nodes, recursive method
            SaveNodes(treeView.Nodes, writer);

            writer.WriteEndElement();

            writer.Close();
            return sb;
        }

        private void SaveNodes(TreeNodeCollection nodesCollection, XmlWriter textWriter)
        {
            for (int i = 0; i < nodesCollection.Count; i++)
            {
                TreeNode node = nodesCollection[i];
                textWriter.WriteStartElement(XmlNodeTag);
                textWriter.WriteAttributeString(XmlNodeTextAtt,
                                                           node.Text);
                textWriter.WriteAttributeString(
                    XmlNodeImageIndexAtt, node.ImageIndex.ToString());
                textWriter.WriteAttributeString(
                    XmlNodeSelectedImageIndexAtt, node.ImageIndex.ToString());

                textWriter.WriteStartElement(XmlNodeTagAtt);

                if (node.Tag != null)
                {
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
                    settings.Indent = true;
                    settings.OmitXmlDeclaration = true;

                    XmlSerializer serializer = _importer.GetXmlSerializer();//new XmlSerializer(typeof(DataNode));

                    StringBuilder sb = new StringBuilder();
                    using (XmlWriter writer = XmlWriter.Create(sb, settings))
                    {
                        serializer.Serialize(writer, node.Tag);

                        textWriter.WriteRaw(sb.ToString());
                    }

                    //textWriter.WriteAttributeString(XmlNodeTagAtt, node.Tag.ToString());
                    // add other node properties to serialize here  
                }

                textWriter.WriteEndElement();

                if (node.Nodes.Count > 0)
                {
                    SaveNodes(node.Nodes, textWriter);
                }
                textWriter.WriteEndElement();
            }
        }

        public void Save(string filename, TreeView sourceTreeView, TreeView targetTreeView, ActionItemHandler _exportHandler)
        {
            XmlTextWriter writer = new XmlTextWriter(filename, System.Text.Encoding.ASCII);
            // writing the xml declaration tag
            writer.WriteStartDocument();
            //textWriter.WriteRaw("\r\n");
            // writing the main tag that encloses all node tags
            writer.WriteStartElement("ProjectPersistence");

            var sb = SerializeTreeView(sourceTreeView, "SourceTreeView");

            writer.WriteRaw(sb.ToString());

            sb = SerializeTreeView(targetTreeView, "TargetTreeView");

            writer.WriteRaw(sb.ToString());

            writer.WriteRaw(_exportHandler.Save().ToString());

            writer.WriteEndElement();

            writer.Close();
        }

        public void Load(string filename, TreeView sourceTreeView, TreeView targetTreeView, ActionItemHandler _exportHandler)
        {
            XmlTextReader reader = new XmlTextReader(filename);

            XmlDocument doc = new XmlDocument();
            doc.Load(filename);

            var stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("SourceTreeView");
                sourceNode.WriteTo(writer);
                writer.Flush();
                stream.Position = 0;
            }

            DeserializeTreeView(sourceTreeView, stream);

            stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("TargetTreeView");
                sourceNode.WriteTo(writer);
                writer.Flush();
                stream.Position = 0;
            }

            DeserializeTreeView(targetTreeView, stream);

            stream = new MemoryStream();
            using (XmlWriter writer = XmlWriter.Create(stream))
            {
                XmlNode sourceNode = doc.DocumentElement.SelectSingleNode("ArrayOfAction");
                if (sourceNode != null)
                {
                    sourceNode.WriteTo(writer);
                    writer.Flush();
                    stream.Position = 0;
                    _exportHandler.LoadXml(stream);
                }
            }
        }


    }
}
